const reportUrl = "http://www.test.com/report";

class Pool {
  logCache = [];
  failCounts = 0;
  sendRemainLogsSuccess = false;

  reportErrorTimer = null;
  loopTimer = null;
  readCacheTimer = null;

  constructor(options) {
    const { duration, reportSize } = options;

    this.duration = duration;
    this.reportSize = reportSize;

    this.loopCheck();
    this.bindEvents();
    this.readIndexdb();
  }

  loopCheck() {
    if (this.loopTimer) {
      return;
    }
    this.loopTimer = setTimeout(() => {
      this.loopTimer = null;
      this.loopCheck();
      if (this.logCache?.length) {
        this.report();
      }
    }, this.duration);
  }

  bindEvents() {
    window.addEventListener("beforeunload", this.sendRemainLogs);
    window.addEventListener("pagehide", this.sendRemainLogs);
    window.addEventListener("unload", this.sendRemainLogs);

    // IOS14 之前不会冒泡，只能监听document
    document.addEventListener("visibilitychange ", () => {
      if (document.visibilityState !== "visible") {
        this.sendRemainLogs();
      } else {
        // 如果界面又显示了，说明没有关闭，重置标志位
        this.sendRemainLogsSuccess = false;
      }
    });
  }

  sendRemainLogs = () => {
    if (this.sendRemainLogsSuccess || !this.logCache?.length) {
      return;
    }

    if (!navigator.sendBeacon) {
      this.saveIndexdb();
      return;
    }

    const logs = this.packLogs(true);
    const result = navigator.sendBeacon(
      reportUrl + "?beacon",
      JSON.stringify(logs)
    );
    if (!result) {
      this.saveIndexdb();
    }
  };

  add(data) {
    this.logCache.push(data);
  }

  saveIndexdb(data) {
    indexdbStore.add("fail_logs", data);

    if (this.readCacheTimer) {
      return;
    }

    this.readCacheTimer = setTimeout(() => {
      this.readIndexdb();
    }, 3000);
  }

  readIndexdb() {
    const logs = indexdbStore.get("fail_logs");

    if (logs?.length) {
      this.logCache.push(...logs);
      indexdbStore.clear("fail_logs");
    }
  }

  packLogs(isPackAll = false) {
    const logs = isPackAll
      ? this.logCache
      : this.logCache.splice(0, this.reportSize);

    if (isPackAll) {
      this.logCache = [];
    }

    return logs || [];
  }

  report() {
    const logs = this.packLogs();
    fetch(reportUrl, {
      method: "POST",
      body: JSON.stringify(logs),
    })
      .then((res) => {
        if (res.ok) {
          return res.json();
        }
        return Promise.reject(res);
      })
      .then((json) => {
        if (json.code !== 200) {
          return Promise.reject(new Error(`上报返回错误${data.code}`));
        }
      })
      .catch((e) => {
        this.handlerReportError();

        if (this.failCounts < 3 && logs?.length) {
          this.logCache.push(...logs);
        } else {
          this.saveIndexdb();
        }
      });
  }

  handlerReportError() {
    this.failCounts++;
    clearTimeout(this.reportErrorTimer);
    this.reportErrorTimer = setTimeout(() => {
      this.failCounts = 0;
      this.reportErrorTimer = null;
    }, 3000);
  }
}

const pool = new Pool({
  duration: 100000,
  reportSize: 20,
});

function report(data) {
  pool.add({
    ...data,
    project: "神仙朱",
    time: Date.now(),
  });
}
